﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Documents
//  File name:	DocConverter.cs
//  Developer:	Solomatov Igor
//  Created:	16.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.SharePoint;
using Smart.Common;

namespace Smart.Documents.Services
{
	public class DocConverter : IDocConverter
	{
		/// <summary>
		/// Extract extension from file url
		/// </summary>
		public virtual string GetExtension(string source)
		{
			return
				source.IsNullOrEmpty()
					? string.Empty
					: source.SubstringFromLast('.');
		}

		/// <summary>
		/// Return extension for <paramref name="format"/>
		/// </summary>
		public virtual string GetExtension(FileFormat format)
		{
			var ext = string.Empty;

			switch (format)
			{
				case FileFormat.Document:
					ext = "docx";
					break;

				case FileFormat.DocumentMacroEnabled:
					ext = "docm";
					break;

				case FileFormat.Template:
					ext = "dotx";
					break;

				case FileFormat.TemplateMacroEnabled:
					ext = "dotm";
					break;

				case FileFormat.Document97:
					ext = "doc";
					break;

				case FileFormat.Template97:
					ext = "dot";
					break;

				case FileFormat.MHTML:
					ext = "mht";
					break;

				case FileFormat.PDF:
					ext = "pdf";
					break;

				case FileFormat.RTF:
					ext = "rtf";
					break;

				case FileFormat.XML:
					ext = "xml";
					break;

				case FileFormat.XPS:
					ext = "xps";
					break;
			}

			return ext;
		}

		/// <summary>
		/// Return format for file url
		/// </summary>
		public virtual FileFormat GetFormat(string source)
		{
			switch (GetExtension(source).ToLower())
			{
				case "mhtm":
				case "mhtml":
				case "html":
				case "htm":
					return FileFormat.MHTML;

				case "rtf":
					return FileFormat.RTF;

				case "docx":
					return FileFormat.Document;

				case "docm":
					return FileFormat.DocumentMacroEnabled;

				case "dotx":
					return FileFormat.Template;

				case "dotm":
					return FileFormat.TemplateMacroEnabled;

				case "doc":
					return FileFormat.Document97;

				case "dot":
					return FileFormat.Template97;

				case "pdf":
					return FileFormat.PDF;

				case "xml":
					return FileFormat.XML;

				case "xps":
					return FileFormat.XPS;
			}

			return FileFormat.Automatic;
		}

		/// <summary>
		/// Checks whether conversion is required, to save file in specified format
		/// </summary>
		public virtual bool IsConversionRequired(string source, FileFormat format)
		{
			var sourceFormat = GetFormat(source);

			return
				(sourceFormat != FileFormat.Automatic) && (sourceFormat != format);
		}

		protected virtual string ChangeExtension(string source, FileFormat format)
		{
			if (source.IsNullOrEmpty())
				return string.Empty;

			var idx = source.LastIndexOf('.');

			var urlWithoutExtension =
				idx < 0
					? source
					: source.Substring(0, idx + 1);

			return
				urlWithoutExtension + GetExtension(format);
		}

		public Random Rnd = new Random();

		/// <summary>
		/// Copy file to <paramref name="folder"/> and change name to short one
		/// </summary>
		/// <returns>Short name</returns>
		protected virtual string CopyFile(SPWeb web, string source, SPFolder folder)
		{
			return CopyFile(web, source, folder, Rnd.Next().ToStringX() + "." + GetExtension(source));
		}

		/// <summary>
		/// Copy file to <paramref name="folder"/> and change name to <paramref name="fileName"/>
		/// </summary>
		/// <returns>Short name</returns>
		protected virtual string CopyFile(SPWeb web, string source, SPFolder folder, string fileName)
		{
			try
			{
				var file = web.ReadFile(web.GetWebRelativeUrl(source));

				if (file == null)
					return null;

				var newFile = folder.Files.Add(fileName, file, true);

				if (newFile != null && newFile.Exists)
					return newFile.Name;
			}
			catch (Exception x)
			{
				H.LX("DocConverter.CopyFile", "CopyFile source: " + source, x);
			}

			return null;
		}

		/// <summary>
		/// Queue conversion job for all source files
		/// </summary>
		public virtual DocConversionJobInfo QueueFileConversionJob(SPSite site, FileFormat format, bool recycle, params string[] sourceFiles)
		{
			return QueueConversionJob(site, sourceFiles, null, format, recycle);
		}

		/// <summary>
		/// Queue conversion job for all source files
		/// </summary>
		public virtual DocConversionJobInfo QueueFileConversionJob(SPSite site, FileFormat format, bool recycle, params SPFile[] sourceFiles)
		{
			return QueueConversionJob(site, sourceFiles.Select(file => file.GetFullUrl()), null, format, recycle);
		}

		/// <summary>
		/// Queue conversion job for all source files
		/// </summary>
		public virtual DocConversionJobInfo QueueFolderConversionJob(SPSite site, FileFormat format, bool recycle, params string[] sourceFolders)
		{
			return QueueConversionJob(site, null, sourceFolders, format, recycle);
		}

		/// <summary>
		/// Queue conversion job for all source files
		/// </summary>
		public virtual DocConversionJobInfo QueueFolderConversionJob(SPSite site, FileFormat format, bool recycle, params SPFolder[] sourceFolders)
		{
			return QueueConversionJob(site, null, sourceFolders.Select(folder => folder.GetFullUrl()), format, recycle);
		}

		/// <summary>
		/// Queue conversion job for all source files
		/// </summary>
		public virtual DocConversionJobInfo QueueConversionJob(SPSite site, IEnumerable<string> sourceFiles, IEnumerable<string> sourceFolders, FileFormat format, bool recycle)
		{
			throw new NotImplementedException();
			/*
			var proxies = GetWordServiceProxies(site);

			if (proxies == null || proxies.Count <= 0)
				throw new InvalidOperationException("Word Automation Services not configured!");

			var proxy = proxies[0];

			var info = new DocConversionJobInfo { ServiceName = proxy.DisplayName, Recycle = recycle };

			var job =
				new ConversionJob(
					info.ServiceName,
					new ConversionJobSettings
						{
							CompatibilityMode = CompatibilityMode.Current,
							AddThumbnail = true,
							UpdateFields = false,
							OutputFormat = format,
							OutputSaveBehavior = SaveBehavior.AlwaysOverwrite
						})
						{
							Name = "Conversion " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(),
							UserToken = site.UserToken
						};

			info.JobId = job.JobId;

			Rnd = new Random();

			var rootWeb = site.RootWeb;
			var tempLib = (SPDocumentLibrary)rootWeb.GetListByUrl(Const.Converter.ConvertingTemplatesLib);
			var tempFolder = tempLib.RootFolder.GetFolderByPath(true, info.JobId.GetHashCodeX());
			var tempFolderFullUrl = tempFolder.GetFullUrl().EnsureEndsWith("/");

			info.WorkingFolder = tempFolderFullUrl.TrimEnd('/');

			ManagedDisposable<SPWeb> dWeb = null;

			try
			{
				var srcFiles = new List<string>();

				if (sourceFiles != null)
					srcFiles.AddRange(sourceFiles);

				if (sourceFolders != null)
				{
					foreach (var sourceFolder in sourceFolders)
					{
						if (sourceFolder.IsNullOrEmpty())
							continue;

						var srcUrl =
							SPUrlUtility.IsUrlRelative(sourceFolder)
								? site.MakeFullUrl(sourceFolder)
								: sourceFolder;

						if (dWeb == null)
							dWeb = SPH.FindWebByUrl(srcUrl);

						try
						{
							var fldr = dWeb.Disposable.GetFolder(SPH.GetWebRelativeUrl(dWeb, srcUrl));

							if (fldr == null || !fldr.Exists)
								continue;

							foreach (SPFile file in fldr.Files)
							{
								var fileFullUrl = file.GetFullUrl();

								if (IsConversionRequired(fileFullUrl, format))
									srcFiles.Add(fileFullUrl);
							}
						}
						catch (Exception x)
						{
							H.LX("QueueConversionJob.Folder." + srcUrl, x);
						}
					}
				}

				srcFiles.RemoveAll(s => s.IsNullOrEmpty());

				foreach (var srcFile in srcFiles)
				{
					var srcUrl =
						SPUrlUtility.IsUrlRelative(srcFile)
							? site.MakeFullUrl(srcFile)
							: srcFile;

					if (dWeb == null)
						dWeb = SPH.FindWebByUrl(srcUrl);

					var tmpSrc = CopyFile(dWeb, srcUrl, tempFolder);

					if (tmpSrc.IsNullOrEmpty())
						continue;

					var dstUrl = ChangeExtension(srcUrl, format);

					var tmpDst = ChangeExtension(tmpSrc, format);

					try
					{
						job.AddFile(tempFolderFullUrl + tmpSrc, tempFolderFullUrl + tmpDst);

						info.Docs.Add(new DocConversionInfo { SrcUrl = srcUrl, DstUrl = dstUrl, TmpSrc = tmpSrc, TmpDst = tmpDst, IsFolder = false });
					}
					catch (Exception x)
					{
						H.LX("QueueConversionJob.File." + srcUrl, x);
					}
				}
			}
			finally
			{
				if (dWeb != null)
					dWeb.Dispose();
			}

			if (info.Docs.Count > 0)
				job.Start();
			else
				info.JobId = Guid.Empty;

			return info;
			 */
		}

		/// <summary>
		/// Checks whether conversion job completed
		/// </summary>
		public virtual bool CheckConversionJobCompleted(DocConversionJobInfo info)
		{
			if (info == null) throw new ArgumentNullException("info");

			if (info.JobId.IsEmpty())
				return true;

			throw new NotImplementedException();
			/*
			var status = new ConversionJobStatus(info.ServiceName, info.JobId, null);

			return
				(status.Count == status.Succeeded + status.Failed);
			 */
		}

		/// <summary>
		/// Handles conversion job completion (update status, recycle source items)
		/// </summary>
		public virtual void HandleConversionJobCompletion(DocConversionJobInfo info)
		{
			if (info == null) throw new ArgumentNullException("info");

			if (info.JobId.IsEmpty())
				return;

			throw new NotImplementedException();
			/*
			var status = new ConversionJobStatus(info.ServiceName, info.JobId, null);

			ManagedDisposable<SPWeb> dWeb = null;
			SPWeb web = null;

			try
			{
				var rootWeb = SPH.GetSite().RootWeb;

				foreach (var itemInfo in status.GetItems(ItemTypes.Succeeded))
				{
					var tmpSrc = itemInfo.InputFile;
					var tmpDst = itemInfo.OutputFile;

					try
					{
						var tmpDstFile = rootWeb.GetFile(SPH.GetWebRelativeUrl(rootWeb, tmpDst));

						var tmpDstName = tmpDstFile.Name;

						var docConversionInfo = info.Docs.Find(conversionInfo => conversionInfo.TmpDst.EqIC(tmpDstName));

						if (docConversionInfo != null && tmpDstFile.Exists)
						{
							var dstUrl = docConversionInfo.DstUrl;

							if (dWeb == null || web == null)
								web = dWeb = SPH.FindWebByUrl(dstUrl);

							dstUrl = SPH.GetWebRelativeUrl(web, dstUrl);

							var copied = web.SaveFile(dstUrl, tmpDstFile.OpenBinary(SPOpenBinaryOptions.SkipVirusScan), true);
							//var fileName = CopyFile(web, tmpSrc, folder, docConversionInfo.DstUrl.TrimEnd('/').SubstringFromLast('/'));

							if (info.Recycle && copied)
							{
								var srcFile = web.GetFile(SPH.GetWebRelativeUrl(web, docConversionInfo.SrcUrl));

								if (srcFile.Exists)
									srcFile.Recycle();
							}
						}
					}
					catch (Exception x)
					{
						H.LX("HandleConversionJobCompletion.File." + tmpSrc, x);
					}
				}

				var workingFolder = rootWeb.GetFolder(SPH.GetWebRelativeUrl(rootWeb, info.WorkingFolder));

				if (workingFolder != null && workingFolder.Exists)
					workingFolder.Delete();
			}
			finally
			{
				if (dWeb != null)
					dWeb.Dispose();
			}
			 */
		}

		/// <summary>
		/// Wait until conversion succeeds
		/// </summary>
		public virtual void WaitConversion(DocConversionJobInfo info)
		{
			if (info == null) throw new ArgumentNullException("info");

			while (!CheckConversionJobCompleted(info))
			{
				Thread.Sleep(1000);
			}
		}
	}
}